#include <stdint.h>

#include "memenv.h"
#include "dobj.h"

static	uint32_t
hash_string(const char *s, int len)
{
	uint32_t	v;
	int	i;

	for (v = 5381, i = 0; i < len; i++) {
		v = ((v << 5) + v) + s[i];
	}

	return	(v);
}

dobj_ptr_t
alloc_list_dobj(struct memenv *env, dobj_ptr_t *objs, int sz, int dot)
{
	struct	dobj_pair	*p;
	dobj_ptr_t	head, cur;
	int	i;

	head = DOBJ_NULL;
	p = NULL;
	if (dot != 0) {
		sz --;
	}
	for (i = 0; i < sz; i++) {
		MENV_ALLOC_OBJ(env, cur, sizeof(*p));
		if (cur == INVALID_OBJ_IDX) {
			/* memory will be recycled by GC */
			return	(DOBJ_NULL);
		}
		if (head == DOBJ_NULL) {
			head = DOBJ_COMPOSE(DOBJT_PAIR, cur);
		} else {
			p->dobj_cdr = DOBJ_COMPOSE(DOBJT_PAIR, cur);
		}
		p = MENV_GET_OBJ(env, cur);
		p->dobj_car = objs[i];
	}
	if (p != NULL) {
		if (dot != 0) {
			p->dobj_cdr = objs[i];
		} else {
			p->dobj_cdr = DOBJ_NULL;
		}
	}

	return	(head);
}

dobj_ptr_t
alloc_bigint_dobj(struct memenv *env, intptr_t num)
{
	intptr_t	*obj;
	dobj_ptr_t	n;

	MENV_ALLOC_OBJ(env, n, sizeof(*obj));
	if (n == INVALID_OBJ_IDX) {
		return	(DOBJ_NULL);
	}
	obj = MENV_GET_OBJ(env, n);
	*obj = num;
	n = DOBJ_COMPOSE(DOBJT_INT, n);

	return	(n);
}

dobj_ptr_t
alloc_string_dobj(struct memenv *env, const char *str, int len)
{
	struct	dobj_string	*ptr;
	dobj_ptr_t	s;
	int	sz;

	sz = ((len + 0x3) & (~0x3)) + sizeof(*ptr);
	MENV_ALLOC_OBJ(env, s, sz);
	if (s == INVALID_OBJ_IDX) {
		return	(DOBJ_NULL);
	}
	ptr = MENV_GET_OBJ(env, s);
	ptr->dobj_str_length = len;
	memcpy(ptr->dobj_str_data, str, len);
	ptr->dobj_str_data[len] = '\0';
	s = DOBJ_COMPOSE(DOBJT_STRING, s);

	return	(s);
}

dobj_ptr_t
alloc_symbol_dobj(struct memenv *env, const char *str, int len)
{
	struct	dobj_symbol	*ptr;
	dobj_ptr_t	s;
	int	sz;

	sz = ((len + 0x3) & (~0x3)) + sizeof(*ptr);
	MENV_ALLOC_OBJ(env, s, sz);
	if (s == INVALID_OBJ_IDX) {
		return	(DOBJ_NULL);
	}
	ptr = MENV_GET_OBJ(env, s);
	ptr->dobj_sym_length = len;
	ptr->dobj_sym_hash = hash_string(str, len);
	memcpy(ptr->dobj_sym_data, str, len);
	s = DOBJ_COMPOSE(DOBJT_SYMBOL, s);

	return	(s);
}

dobj_ptr_t
alloc_double_dobj(struct memenv *env, double d)
{
	double	*dp;
	dobj_ptr_t	o;

	MENV_ALLOC_OBJ(env, o, sizeof(*dp));
	if (o == INVALID_OBJ_IDX) {
		return	(DOBJ_NULL);
	}
	dp = MENV_GET_OBJ(env, o);
	*dp = d;
	o = DOBJ_COMPOSE(DOBJT_DOUBLE, o);

	return	(o);
}

dobj_ptr_t
alloc_vector_dobj(struct memenv *env, dobj_ptr_t *objs, int n)
{
	struct	dobj_vector	*vect;
	dobj_ptr_t	v;
	int	sz;

	sz = (n * sizeof(*objs)) + sizeof(*vect);
	MENV_ALLOC_OBJ(env, v, sz);
	if (v == INVALID_OBJ_IDX) {
		return	(DOBJ_NULL);
	}
	vect = MENV_GET_OBJ(env, v);
	vect->dobj_vec_length = n;
	memcpy(vect->dobj_vec_data, objs, (n * sizeof(objs[0])));
	v = DOBJ_COMPOSE(DOBJT_VECTOR, v);

	return	(v);
}

